﻿/************************************************************************
 * SMLib - State Model Library
 * Copyright (C) 2012-2013 Ali Khalili (khalili_DOT_ir_@SIGN_gmail_DOT_com)
 * **********************************************************************
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License (LGPL) published 
 * by the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version. This program is distributed without any
 * warranty. See the GNU Lesser General Public License for more details. 
 * 
 * This file is an adoptation of C code: (c) Bob Jenkins
 * http://burtleburtle.net/bob/c/lookup2.c
 * ***********************************************************************
 * File         :     SMLib\Utils\Hashing\HashAlgos_Lookup2.cs
 * Description  :     Lookup2c hash algorithm
 * Change log   :     -Jan 2012:  First version
 * To Do        :     
 ************************************************************************/
using System;
using System.Collections.Generic;


namespace SMLib.Utils.Hashing
{
    /// <summary>
    /// Lookup2c hash algorithm (public domain).
    /// Ref. http://burtleburtle.net/bob/hash/index.html
    /// This is the algorithm used by the SPIN model checker for state hashing. It has the
    /// property that every output bit is dependent on every input bit and is considered
    /// to be a fast good hash.
    /// </summary>
    public class HashAlgos_Lookup2
    {
        /// <summary>
        /// Calculates the hash generatedCode of an object, or a default hash if obj is null
        /// </summary>
        /// <param name="obj">The object, or null</param>
        /// <returns>Hash generatedCode of object, or a default hash if null</returns>
        public static int GetHashCode(object/*?*/ obj)
        {
            return (obj == null ? -831227886 : obj.GetHashCode());
        }

        /// <summary>
        /// Calculates an order-dependent hash generatedCode of an enumeration of values.
        /// </summary>
        /// <param name="initialValue">initial hash</param>
        /// <param name="values">the enumarators of values</param>
        /// <returns>the computed hash</returns>
        public static int ComputeEnumeratedHash(int initialValue, System.Collections.IEnumerator/*?*/ values)
        {
            unchecked
            {
                uint a = 0x9e3779b9;            /* the golden ratio; an arbitrary value */
                uint b = 0x9e3779b9;
                uint c = (uint)initialValue;    /* the previous hash value */
                int length = 0;
                bool repeat = true;

                if (values == null) return initialValue;

                while (repeat)
                {
                    repeat = false;
                    if (values.MoveNext())
                    {
                        a += (uint)GetHashCode(values.Current);
                        length += 1;
                    }
                    if (values.MoveNext())
                    {
                        b += (uint)GetHashCode(values.Current);
                        length += 1;
                    }
                    if (values.MoveNext())
                    {
                        c += (uint)GetHashCode(values.Current);
                        length += 1;
                        repeat = true;
                    }
                    else
                    {
                        c += (uint)length * 4u;
                    }
                    a -= b; a -= c; a ^= (c >> 13);
                    b -= c; b -= a; b ^= (a << 8);
                    c -= a; c -= b; c ^= (b >> 13);
                    a -= b; a -= c; a ^= (c >> 12);
                    b -= c; b -= a; b ^= (a << 16);
                    c -= a; c -= b; c ^= (b >> 5);
                    a -= b; a -= c; a ^= (c >> 3);
                    b -= c; b -= a; b ^= (a << 10);
                    c -= a; c -= b; c ^= (b >> 15);
                }

                return (int)c;
            }
        }
        /// <summary>
        /// Calculates an order-dependent combination from an enumeration of hash codes.
        /// </summary>
        /// <param name="initialValue">The starting value of the hash</param>
        /// <param name="hashCodes">The enumerator of hash codes to be combined</param>
        /// <returns>The order-dependent combination of values</returns>
        public static int CombineHashCodes(int initialValue, IEnumerator<int> hashCodes)
        {
            unchecked
            {
                uint a = 0x9e3779b9;            /* the golden ratio; an arbitrary value */
                uint b = 0x9e3779b9;
                uint c = (uint)initialValue;    /* the previous hash value */
                int length = 0;
                bool repeat = true;

                if (hashCodes == null) return initialValue;

                while (repeat)
                {
                    repeat = false;
                    if (hashCodes.MoveNext())
                    {
                        a += (uint)(hashCodes.Current);
                        length += 1;
                    }
                    if (hashCodes.MoveNext())
                    {
                        b += (uint)(hashCodes.Current);
                        length += 1;
                    }
                    if (hashCodes.MoveNext())
                    {
                        c += (uint)(hashCodes.Current);
                        length += 1;
                        repeat = true;
                    }
                    else
                    {
                        c += (uint)length * 4u;
                    }
                    a -= b; a -= c; a ^= (c >> 13);
                    b -= c; b -= a; b ^= (a << 8);
                    c -= a; c -= b; c ^= (b >> 13);
                    a -= b; a -= c; a ^= (c >> 12);
                    b -= c; b -= a; b ^= (a << 16);
                    c -= a; c -= b; c ^= (b >> 5);
                    a -= b; a -= c; a ^= (c >> 3);
                    b -= c; b -= a; b ^= (a << 10);
                    c -= a; c -= b; c ^= (b >> 15);
                }

                return (int)c;
            }
        }

        ///// <summary>
        ///// Combines previously calculate hash values.
        ///// </summary>
        ///// <returns>The hash value calculated.</returns>
        //public static int CombineHash(int initialValue, params int[] otherValues)
        //{
        //    return Lookup2cHash(otherValues, initialValue);
        //}

        ///// <summary>
        ///// Calculates the hash fingerprint of an integer array.
        ///// </summary>
        ///// <param name="k">The key.</param>
        ///// <param name="initval">The previous hash, or an arbitrary value.</param>
        ///// <returns>The hash value calculated.</returns>
        //private static int Lookup2cHash(int[] k, int initialValue)
        //{
        //    unchecked
        //    {
        //        uint initval = (uint)initialValue;
        //        uint a = 0x9e3779b9; /* the golden ratio; an arbitrary value */
        //        uint b = 0x9e3779b9;
        //        uint c = initval;    /* the previous hash value */
        //        int length = k.Length;
        //        uint len = (uint)length;
        //        int i = 0;

        //        while (len >= 3)
        //        {
        //            a += (uint)k[i];
        //            b += (uint)k[i + 1];
        //            c += (uint)k[i + 2];
        //            a -= b; a -= c; a ^= (c >> 13);
        //            b -= c; b -= a; b ^= (a << 8);
        //            c -= a; c -= b; c ^= (b >> 13);
        //            a -= b; a -= c; a ^= (c >> 12);
        //            b -= c; b -= a; b ^= (a << 16);
        //            c -= a; c -= b; c ^= (b >> 5);
        //            a -= b; a -= c; a ^= (c >> 3);
        //            b -= c; b -= a; b ^= (a << 10);
        //            c -= a; c -= b; c ^= (b >> 15);
        //            i += 3; len -= 3;
        //        }

        //        if (len > 0) a += (uint)k[i];
        //        if (len > 1) b += (uint)k[i + 1];
        //        c += (uint)length * 4u;
        //        a -= b; a -= c; a ^= (c >> 13);
        //        b -= c; b -= a; b ^= (a << 8);
        //        c -= a; c -= b; c ^= (b >> 13);
        //        a -= b; a -= c; a ^= (c >> 12);
        //        b -= c; b -= a; b ^= (a << 16);
        //        c -= a; c -= b; c ^= (b >> 5);
        //        a -= b; a -= c; a ^= (c >> 3);
        //        b -= c; b -= a; b ^= (a << 10);
        //        c -= a; c -= b; c ^= (b >> 15);

        //        return (int)c;
        //    }
        //} 


    }
}
